package domain;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.NetworkInterface;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedList;

import javax.swing.table.TableModel;

import serializables.AddSongCommand;
import serializables.Command;
import serializables.ServerStatus;
import serializables.Song;

import gui.GuiController;

public class ClientDomainController 
{
	// Attributen
	private static final int PORT = 4445;
	private static final String MULTICAST_ADDRESS = "230.0.0.1";
	private static final int REQUEST_UPDATE = 0;
	private static final int COMMAND_PORT = 1236;
	private static final int PLAYLIST_PORT = 4446;
	private static GuiController gc;
	private MulticastSocket multicastSocket;
	
	
	//TODO beter georderd zetten
	private static DatagramSocket dgramSocket;
	private static DatagramPacket inPacket;
	private static DatagramPacket outPacket;
	private Object host;
	private static String serverIp;
	public static String myOwnIp;
	private long currentServerStatusId;
	private static byte[] buffer;
	private static ServerStatus serverStatus;
	private static LinkedList<Song> songs;
	private static domain.SongTableModel songTableModel;

	/**
	 * Runs the application
	 * 
	 * @access public
	 * @return void
	 */
	public void run() {
		myOwnIp = getLocalIpAddress();
		System.out.println(getLocalIpAddress());
		songs = new LinkedList<Song>();

		setUpClientSideMulticast();
		
		gc = new GuiController(this);
		gc.startGui();
		//host = InetAddress.getLocalHost();
	}

	/**
	 * Sets up the client side multicast
	 * 
	 * @access private
	 * @return void
	 */
	private void setUpClientSideMulticast() {
		CheckServerStatusThread thread = new CheckServerStatusThread(MULTICAST_ADDRESS, PORT);
		thread.start();
		
		ReceivePlaylistThread rpt = new ReceivePlaylistThread(MULTICAST_ADDRESS, PLAYLIST_PORT);
		rpt.start();
		/*try
		{
			
			//TODO put all this in one thread that always checks the ServerStatusId;
			multicastSocket = new MulticastSocket(PORT);
			InetAddress address = InetAddress.getByName(MULTICAST_ADDRESS);
			
			multicastSocket.joinGroup(address);
			System.out.println("joined group " + address);
			
			DatagramPacket serverIpPacket;
			byte[] buf = new byte[256];
			serverIpPacket = new DatagramPacket(buf, buf.length);
			while(receivedIp == null){
				System.out.println("waiting to receive");
				multicastSocket.receive(serverIpPacket);
				receivedIp = serverIpPacket.getAddress().getHostAddress();
				ByteBuffer buffer = ByteBuffer.wrap(serverIpPacket.getData());
				currentServerStatusId = buffer.getLong();
				System.out.println("received current ServerStatusId: " + currentServerStatusId);
			}
			
		}
		catch (IOException e) {
			System.out.println("Exception on joining multicast: "+e.getMessage());
		}*/
	}

	public String getReceivedIp() {
		return serverIp;
	}
	
	
	private static String getLocalIpAddress() {
		try {
			
			String hostName = InetAddress.getLocalHost().getHostName();
			InetAddress addrs[] = InetAddress.getAllByName(hostName);
			String myIp = "UNKNOWN";
			for (InetAddress addr: addrs) {
			System.out.println ("addr.getHostAddress() = " + addr.getHostAddress());
			System.out.println ("addr.getHostName() = " + addr.getHostName());
			System.out.println ("addr.isAnyLocalAddress() = " + addr.isAnyLocalAddress());
			System.out.println ("addr.isLinkLocalAddress() = " + addr.isLinkLocalAddress());
			System.out.println ("addr.isLoopbackAddress() = " + addr.isLoopbackAddress());
			System.out.println ("addr.isMulticastAddress() = " + addr.isMulticastAddress());
			System.out.println ("addr.isSiteLocalAddress() = " + addr.isSiteLocalAddress());
			System.out.println ("");

			hostName = addr.getHostName();
			if (!addr.isLoopbackAddress() && addr.isSiteLocalAddress()) {
			myIp = addr.getHostAddress();
			}
			}
			System.out.println ("\nIP = " + myIp);
			return myIp;
			
		} catch (UnknownHostException e) {
		}
		return null;
	}

	public static long getCurrentServerStatusId() {
		if(serverStatus==null)
			return 0;
		return serverStatus.getVersionId();
	}

	public static void requestServerStatusUpdate() {
		try{
			
			//send request for serverstatus update
			//command is send as an Integer, a constant
			dgramSocket = new DatagramSocket();
			byte[] commandIntBuffer = ByteBuffer.allocate(4).putInt(REQUEST_UPDATE).array();
			outPacket = new DatagramPacket(commandIntBuffer, commandIntBuffer.length,
					InetAddress.getByName(serverIp), COMMAND_PORT);
			dgramSocket.send(outPacket);
			
			
			byte[] receiveBuffer = new byte[256];
			inPacket = new DatagramPacket(receiveBuffer, receiveBuffer.length);
			dgramSocket.receive(inPacket);
			
			//TODO receive serverStatus with objectinputstream
			ByteArrayInputStream bais = new ByteArrayInputStream(receiveBuffer);
			
			ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(bais));
			serverStatus = (ServerStatus) ois.readObject();
			//test
			System.out.println("random text = " +serverStatus.getRandomText());
			
			
		}catch (IOException e) {
			// TODO: handle exception
		} catch (ClassNotFoundException e) {
			System.out.println("Class not found on request update: " + e);
		}
		
	}

	public static void setServerIp(String receivedIp) {
		serverIp = receivedIp;
		
	}

	public static void setSongs(LinkedList<Song> readObject) {
		songs = readObject;
		System.out.println("received playlist, first song: " + songs.getFirst().getTitle());
		
		songTableModel.setSongs(readObject);
		songTableModel.fireTableDataChanged();
		//gc.updatePlaylist();
	}

	public static TableModel getSongTableModel() {
		if(songTableModel==null){
			songTableModel = new SongTableModel(songs);
		}
		return songTableModel;
	}

	public static void sendSongToServer(Song song) {
		AddSongCommand command = new AddSongCommand(song);
		try{
			ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
			ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(baos));
			
			oos.flush();
			oos.writeObject(command);
			oos.flush();
			
			//retrieves byte array
			byte[] sendBuffer = baos.toByteArray();
			InetAddress serverInet;
			serverInet = InetAddress.getByName(serverIp);
			outPacket = new DatagramPacket(sendBuffer, sendBuffer.length, serverInet, COMMAND_PORT);
			dgramSocket.send(outPacket);
	
			oos.close();
		}catch(IOException e){
			
		}
		
	}
	
	

}
